/**
	Pulsar engine. Core.
	Virtual file manager

	Copyright (C) 2013-2014 Dmitry Dolgov

	This program is free software; you can redistribute it and/or modify it under the terms of the
	GNU General Public License as published by the Free Software Foundation; either version 2 of
	the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
	without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
	See the GNU General Public License for more details.
*/

#include <pulsar/pulsar.h>
#include <pulsar/src/pulsar_intr.h>

namespace pl
{

FileManagerImpl* FileManager::_pImpl;

} // namespace pl

/**
	Initialize file manager
	Internal function: called from core initialization
*/
bool pl::FileManager::_init()
{
	if (_pImpl != NULL)
	{
		PL_ASSERTE(false, PL_T("Secondary initialization"));
		return true;
	}
	plLogDebug(PL_LOG_NAME, PL_T("Creating file manager"));

	_pImpl = FileManagerImpl::createImpl();

	if (_pImpl == NULL || !_pImpl->createDrives())
	{
		PL_DELETE _pImpl;
		_pImpl = NULL;

		plLogError(PL_LOG_NAME, PL_T("Can't create/initialize file implementation"));
		return false;
	}

	// Register standard disks
	if (!createTranslateDisk(pl::AppBase::appBase->getStdPath(pl::STANDARD_PATH_APPROOT), PL_T("exe")))
	{
		plLogError(PL_LOG_NAME, PL_T("Translation path 'exe:' can't be assigned"));
	}
	if (!createTranslateDisk(pl::AppBase::appBase->getStdPath(pl::STANDARD_PATH_TEMP), PL_T("tmp")))
	{
		plLogError(PL_LOG_NAME, PL_T("Translation path 'tmp:' can't be assigned"));
	}

	plLogDebug(PL_LOG_NAME, PL_T("File manager succesfully initialized"));
	return true;
}

/**
	Shutdown file manager
	Internal function: called from core shutdown
*/
void pl::FileManager::_term()
{
	if (_pImpl == NULL)
	{
		PL_ASSERTE(false, PL_T("Secondary shutdown"));
		return;
	}

	PL_DELETE _pImpl;
	_pImpl = NULL;
	return;
}

/**
	Find a drive by filepath
*/
pl::Drive* pl::FileManager::_findDrive(const pl::Filepath& path)
{
	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return NULL;
	}

	pl::ListEnumerator<Drive> enumForw(*_pImpl);
	while (!enumForw.isFinished())
	{
		if (enumForw->isAccepted(path))
		{
			return enumForw;
		}
	}
	return NULL;
}

/**
	Find a drive and change it to real drive
	This function is used for virtual drives like "exe:\\", etc
*/
pl::Drive* pl::FileManager::_findHandleDrive(const pl::Filepath& path, pl::Filepath& pathResult)
{
	pathResult = path;

	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return NULL;
	}

	pl::Drive* pDrive = _findDrive(pathResult);

	if (pDrive == NULL && _pImpl->convertToFullPath(pathResult))
	{
		// Can't find drive - probably - relative path used?
		pDrive = _findDrive(pathResult);
	}
	if (pDrive == NULL)
	{
		// Still can't find a drive
		return NULL;
	}

	if (pDrive->handleTrasnlation(pathResult))
	{
		// This is a virtual drive - convert it to read drive
		pDrive = _findDrive(pathResult);
	}
	if (pDrive == NULL)
	{
		return NULL;
	}

	// Perform additional drive-specific conversion if need
	pDrive->applyPlatformRules(pathResult);
	return pDrive;
}

/**
	\param rootPath real root path
	\param prefix virtual disk name (like xxx)
	Create virtual disk to access files using xxx:\ prefix
*/
bool pl::FileManager::createTranslateDisk(const pl::Filepath& rootPath, const pl::String<4>& prefix)
{
	PL_ASSERTW(prefix.size() == 3, PL_T("Wrong prefix length"));

	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return 0;
	}

	PL_NEW DriveTranslate(rootPath, prefix);
	return true;
}

/**
	\param path - path name, file name or root drive name
	\param nBytes return value
	\return pl::FILE_ERROR_NONE or error code
	Calculate total disk space
*/
pl::FileError pl::FileManager::getTotalDiskSpace(const pl::Filepath& path, pl_uint64 &nBytes)
{
	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return pl::FILE_ERROR_NOT_INITED;
	}

	pl::Filepath pathResult;
	pl::Drive* pDrive = _findHandleDrive(path, pathResult);
	if (pDrive == NULL)
	{
		return pl::FILE_ERROR_DRIVE_NOT_FOUND;
	}
	return pDrive->getTotalDiskSpace(nBytes);
}

/**
	\param path - path name, file name or root drive name
	\param nBytes return value
	\return pl::FILE_ERROR_NONE or error code
	Calculate free disk space
*/
pl::FileError pl::FileManager::getFreeDiskSpace(const pl::Filepath& path, pl_uint64 &nBytes)
{
	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return pl::FILE_ERROR_NOT_INITED;
	}

	pl::Filepath pathResult;
	pl::Drive* pDrive = _findHandleDrive(path, pathResult);
	if (pDrive == NULL)
	{
		return pl::FILE_ERROR_DRIVE_NOT_FOUND;
	}
	return pDrive->getFreeDiskSpace(nBytes);
}

/**
	\param oldName Old file name
	\param newName New file name
	\return pl::FILE_ERROR_NONE or error code
	Rename file (both files must be located on the same drive)
*/
pl::FileError pl::FileManager::renameFile(const pl::Filepath& oldName, const pl::Filepath& newName)
{
	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return pl::FILE_ERROR_NOT_INITED;
	}
	pl::Filepath oldNameResult;
	pl::Filepath newNameResult;

	pl::Drive* pDrive1 = _findHandleDrive(oldName, oldNameResult);
	pl::Drive* pDrive2 = _findHandleDrive(newName, newNameResult);
	if (pDrive1 == NULL || pDrive2 == NULL)
	{
		return pl::FILE_ERROR_DRIVE_NOT_FOUND;
	}
	if (pDrive1 != pDrive2)
	{
		return pl::FILE_ERROR_RENAME_DRIVE;
	}

	return pDrive1->renameFile(oldNameResult, newNameResult);
}

/**
	\param oldName Old file name
	\param newName New file name
	\return pl::FILE_ERROR_NONE or error code
	Copy file
*/
pl::FileError pl::FileManager::copyFile(const pl::Filepath& oldName, const pl::Filepath& newName, bool bForce /*= false*/)
{
	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return pl::FILE_ERROR_NOT_INITED;
	}

	return _pImpl->copyFile(oldName, newName, bForce);
}

/**
	\param fileName file to delete
	\return pl::FILE_ERROR_NONE or error code
	Remove file
*/
pl::FileError pl::FileManager::deleteFile(const pl::Filepath& fileName)
{
	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return pl::FILE_ERROR_NOT_INITED;
	}
	pl::Filepath fileNameResult;

	pl::Drive* pDrive = _findHandleDrive(fileName, fileNameResult);
	if (pDrive == NULL)
	{
		return pl::FILE_ERROR_DRIVE_NOT_FOUND;
	}
	return pDrive->deleteFile(fileNameResult);
}

/**
	\param dirName Full directory name
	\param bAllowRecursive true, if fuction can create full dir tree
	\return pl::FILE_ERROR_NONE or error code
	Create a subfolder
*/
pl::FileError pl::FileManager::makeDir(const pl::Filepath& dirName, bool bAllowRecursive /*= false*/)
{
	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return pl::FILE_ERROR_NOT_INITED;
	}

	pl::Filepath dirNameResult;
	pl::Drive* pDrive = _findHandleDrive(dirName, dirNameResult);
	if (pDrive == NULL)
	{
		return pl::FILE_ERROR_DRIVE_NOT_FOUND;
	}
	pl::FileError err = pDrive->makeDir(dirNameResult);

	switch (err)
	{
		case pl::FILE_ERROR_NONE:
		case pl::FILE_ERROR_DIRECTORY_EXIST:
			return err;
		default:
			if (!bAllowRecursive)
			{
				return err;
			}
			break;
	}

	pl::Filepath parentPath = pl::removeFilename(dirNameResult);
	if (parentPath.empty())
	{
		return err;
	}
	err = makeDir(parentPath, true);
	if (err == pl::FILE_ERROR_NONE)
	{
		// Create current directory
		return pDrive->makeDir(dirNameResult);
	}
	return err;

}

/**
	\param dirName folder name
	\param options DELETE_DIR_OPTIONS_*** flags
	\return pl::FILE_ERROR_NONE or error code
	Removed folder (with content, if need) or remove all content from folders
*/
pl::FileError pl::FileManager::deleteDir(const pl::Filepath& dirName, pl_uint32 options /*= 0*/)
{
	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return pl::FILE_ERROR_NOT_INITED;
	}

	pl::Filepath dirNameResult;
	pl::Drive* pDrive = _findHandleDrive(dirName, dirNameResult);
	if (pDrive == NULL)
	{
		return pl::FILE_ERROR_DRIVE_NOT_FOUND;
	}


	if (options == 0)
	{
		return pDrive->deleteDir(dirNameResult);
	}

	if (options & (DELETE_DIR_OPTIONS_RECURSIVE | DELETE_DIR_OPTIONS_CLEAR_FILES))
	{
		// Delete with recussion
		pl::FindFile * pFind = findFile(pl::mergeFilename(dirNameResult, PL_T("*.*")));

		while (pFind != NULL && !pFind->isFinished())
		{
			if ((options & DELETE_DIR_OPTIONS_RECURSIVE) && (pFind->getFileAttrib() & FILE_ATTR_DIRECTORY))
			{
				if (pFind->getFileName() == PL_T(".") || pFind->getFileName() == PL_T(".."))
				{
					continue;
				}

				pl::Filepath newDir = pl::mergeFilename(dirNameResult, pFind->getFileName());
				pl::FileError rc = deleteDir(newDir, options & ~DELETE_DIR_OPTIONS_KEEP_TOP_DIR);
				if (rc != pl::FILE_ERROR_NONE)
				{
					pFind->release();
					return rc;
				}
			}
			else if ((options & DELETE_DIR_OPTIONS_CLEAR_FILES) && !(pFind->getFileAttrib() & FILE_ATTR_DIRECTORY))
			{
				pDrive->deleteFile(pl::mergeFilename(dirNameResult, pFind->getFileName()));
			}
		}
		pFind->release();
	}

	if ((options & (DELETE_DIR_OPTIONS_KEEP_TOP_DIR | DELETE_DIR_OPTIONS_KEEP_ALL_DIR)) == 0)
	{
		return pDrive->deleteDir(dirNameResult);
	}
	return pl::FILE_ERROR_NONE;
}

/**
	\param source Source folder
	\param dest Destination folder
	\param bRecursive true, if include subfolders
	\return pl::FILE_ERROR_NONE or error code
	Copy all files from source to destination
*/
pl::FileError pl::FileManager::copyDir(const pl::Filepath& source, const pl::Filepath& dest, bool bRecursive)
{
	pl::StdVector<pl::Filepath> list;
	pl::Filepath sourcePath = pl::removeFilename(source);
	pl_uint32 i;

	if (!pl::FindFile::findFiles(list, source, bRecursive))
	{
		return pl::FILE_ERROR_FILE_NOT_FOUND;
	}

	for (i = 0;i < list.size(); ++i)
	{
		pl::Filepath dstRelative;
		pl::Filepath dstFileName;
		pl::Filepath dstFilePath;
		pl::FileError err;

		pl::makeRelativePath(sourcePath, list[i], dstRelative);

		dstFileName = pl::mergeFilename(dest, dstRelative);
		dstFilePath = pl::removeFilename(dstFileName);

		pl::FileManager::deleteFile(dstFileName);

		if (pl::FileManager::checkFileExist(dstFilePath) == pl::FILE_ERROR_FILE_NOT_FOUND)
		{
			pl::FileManager::makeDir(dstFilePath, true);
		}
		err = pl::FileManager::copyFile(list[i], dstFileName, false);
		if (err != pl::FILE_ERROR_NONE)
		{
			plLogWarn(PL_LOG_NAME, PL_T("Can't copy ") << list[i] << PL_T(" to ") << dstFileName << PL_T("; code ") << err);
			return err;
		}
	}
	return pl::FILE_ERROR_NONE;
}

/**
	\param fileName name of file
	\return pl::FILE_ERROR_NONE or error code
	Check file existance
	pl::FILE_ERROR_NONE - file is present
	pl::FILE_ERROR_FILE_NOT_FOUND - file is absent
	pl::FILE_ERROR_DRIVE_NOT_FOUND - no such drive
*/
pl::FileError pl::FileManager::checkFileExist(const pl::Filepath& fileName)
{
	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return pl::FILE_ERROR_NOT_INITED;
	}

	pl::Filepath fileNameResult;
	pl::Drive* pDrive = _findHandleDrive(fileName, fileNameResult);
	if (pDrive == NULL)
	{
		return pl::FILE_ERROR_DRIVE_NOT_FOUND;
	}

	return pDrive->checkFileExist(fileNameResult);
}

/**
	\param fileName name of file
	\param attrib - return value
	\return pl::FILE_ERROR_NONE or error code
	Get file attributes
*/
pl::FileError pl::FileManager::getFileAttrib(const pl::Filepath& fileName, pl_uint32& attrib)
{
	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return pl::FILE_ERROR_NOT_INITED;
	}

	pl::Filepath fileNameResult;
	pl::Drive* pDrive = _findHandleDrive(fileName, fileNameResult);
	if (pDrive == NULL)
	{
		return pl::FILE_ERROR_DRIVE_NOT_FOUND;
	}

	return pDrive->getFileAttrib(fileNameResult, attrib);
}

/**
	\param fileName name of file
	\param attrib - FILE_ATTR_*** attributes
	\return pl::FILE_ERROR_NONE or error code
	Set file attributes
*/
pl::FileError pl::FileManager::setFileAttrib(const pl::Filepath& fileName, pl_uint32 attrib)
{
	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return pl::FILE_ERROR_NOT_INITED;
	}

	pl::Filepath fileNameResult;
	pl::Drive* pDrive = _findHandleDrive(fileName, fileNameResult);
	if (pDrive == NULL)
	{
		return pl::FILE_ERROR_DRIVE_NOT_FOUND;
	}

	return pDrive->setFileAttrib(fileNameResult, attrib);
}

/**
	\param fileName name of file
	\param size - return value
	\return pl::FILE_ERROR_NONE or error code
	Calculate file size
*/
pl::FileError pl::FileManager::getFileSize(const pl::Filepath& fileName, pl_int64& size)
{
	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return pl::FILE_ERROR_NOT_INITED;
	}

	pl::Filepath fileNameResult;
	pl::Drive* pDrive = _findHandleDrive(fileName, fileNameResult);
	if (pDrive == NULL)
	{
		return pl::FILE_ERROR_DRIVE_NOT_FOUND;
	}

	return pDrive->getFileSize(fileNameResult, size);
}

/**
	\param fileName name of file
	\param size - new file size
	\return pl::FILE_ERROR_NONE or error code
	Salculate new file size
*/
pl::FileError pl::FileManager::setFileSize(const pl::Filepath& fileName, pl_int64 size)
{
	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return pl::FILE_ERROR_NOT_INITED;
	}

	pl::Filepath fileNameResult;
	pl::Drive* pDrive = _findHandleDrive(fileName, fileNameResult);
	if (pDrive == NULL)
	{
		return pl::FILE_ERROR_DRIVE_NOT_FOUND;
	}

	return pDrive->setFileSize(fileNameResult, size);
}

/**
	\param fileName name of file
	\param type - date type (creation, modification, aceess)
	\param date - return value
	\return pl::FILE_ERROR_NONE or error code
	Query file date.
	Note some date types may not be supported by drive or OS
*/
pl::FileError pl::FileManager::getFileDate(const pl::Filepath& fileName, pl::FileDateType type, pl::DateTime& date)
{
	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return pl::FILE_ERROR_NOT_INITED;
	}

	pl::Filepath fileNameResult;
	pl::Drive* pDrive = _findHandleDrive(fileName, fileNameResult);
	if (pDrive == NULL)
	{
		return pl::FILE_ERROR_DRIVE_NOT_FOUND;
	}

	return pDrive->getFileDate(fileNameResult, type, date);
}

/**
	\param fileName name of file
	\param type - date type (creation, modification, aceess)
	\param date - new file date
	\return pl::FILE_ERROR_NONE or error code
	Query file date.
	Note some date types may not be supported by drive or OS
	Note fome OS/FAT may ignore seconds
*/
pl::FileError pl::FileManager::setFileDate(const pl::Filepath& fileName, pl::FileDateType type, const pl::DateTime& date)
{
	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return pl::FILE_ERROR_NOT_INITED;
	}

	pl::Filepath fileNameResult;
	pl::Drive* pDrive = _findHandleDrive(fileName, fileNameResult);
	if (pDrive == NULL)
	{
		return pl::FILE_ERROR_DRIVE_NOT_FOUND;
	}

	return pDrive->setFileDate(fileName, type, date);
}

/**
	\param mask mask to looking for files
	\return pointer to search object
	Open handle to find files
*/
pl::FindFile* pl::FileManager::findFile(const pl::Filepath& mask)
{
	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return NULL;
	}

	pl::Filepath maskResult;
	pl::Drive* pDrive = _findHandleDrive(mask, maskResult);
	if (pDrive == NULL)
	{
		return NULL;
	}
	return pDrive->findFile(maskResult);
}

/**
	\param fileName name of file
	\param share OPEN_FILE_SHARE_*** mode
	\param createAttrib OPEN_FILE_*** attrubutes
	\return NULL if error occured
	Open file as a stream (for reading)
*/
pl::StreamRead* pl::FileManager::openFileRead(const pl::Filepath& fileName, pl_uint32 shareAndAttrib)
{
	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return NULL;
	}

	pl::Filepath fileNameResult;
	pl::Drive* pDrive = _findHandleDrive(fileName, fileNameResult);
	if (pDrive == NULL)
	{
		return NULL;
	}

	return pDrive->openFileRead(fileNameResult, shareAndAttrib);
}

/**
	\param fileName name of file
	\param share OPEN_FILE_SHARE_*** mode
	\param createAttrib OPEN_FILE_*** attrubutes
	\return NULL if error occured
	Open file as a stream (for writing)
*/
pl::StreamWrite* pl::FileManager::openFileWrite(const pl::Filepath& fileName, pl_uint32 shareAndAttrib)
{
	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File system was not initialized"));
		return NULL;
	}

	pl::Filepath fileNameResult;
	pl::Drive* pDrive = _findHandleDrive(fileName, fileNameResult);
	if (pDrive == NULL)
	{
		return NULL;
	}

	return pDrive->openFileWrite(fileNameResult, shareAndAttrib);
}

/**
	\param fileName file name
	\param pBuffer pointer to preallocated buffer
	\param bufSize size of buffer (will be truncated with actual file size)
	\return pl::FILE_ERROR_NONE or error code
	Load file into the memory
*/
pl::FileError pl::FileManager::loadFileMem(const pl::Filepath& fileName, void* pBuffer, pl_int64& bufSize)
{
	pl_int64 sizeFile;
	pl::FileError rc;

	rc = pl::FileManager::getFileSize(fileName, sizeFile);
	if (rc != pl::FILE_ERROR_NONE)
	{
		return rc;
	}

	if (bufSize < sizeFile)
	{
		return pl::FILE_ERROR_OVERFLOW;
	}
	pl::StreamRead* s = pl::FileManager::openFileRead(fileName, pl::OPEN_FILE_SHARE_READ + pl::OPEN_FILE_OPENEXIST);
	if (s == NULL)
	{
		return pl::FILE_ERROR_FILE_NOT_FOUND;
	}

	bufSize = sizeFile;
	while (sizeFile > 0)
	{
		pl_int64 sizeReadCur = pl::min<pl_int64>(sizeFile, 100000);

		if (!s->readBuffer(pBuffer, 1, (pl_int32)sizeReadCur))
		{
			PL_DELETE s;
			return pl::FILE_ERROR_READ;
		}
		sizeFile -= sizeReadCur;
		pBuffer = (pl_uint8*)pBuffer + sizeReadCur;
	}
	PL_DELETE s;
	return pl::FILE_ERROR_NONE;


}

/**
	\param fileName file name
	\param pBufSizeActual size of file (return value)
	\return pointer to buffer or NULL
	Load file into the memory
	Note use free_static() to release used memory
*/
void* pl::FileManager::loadFileMem(const pl::Filepath& fileName, pl_int64& pBufSizeActual)
{
	pl_int64 size;

	if (pl::FileManager::getFileSize(fileName, size) != pl::FILE_ERROR_NONE)
	{
		return NULL;
	}
	if (size > PL_MAX_ALLOCATION_SIZE)
	{
		return NULL;
	}

	void* p = malloc_static((size_t)size);
	if (p == NULL)
	{
		return NULL;
	}

	if (loadFileMem(fileName, p, size) != pl::FILE_ERROR_NONE)
	{
		free_static(p);
		return NULL;
	}
	pBufSizeActual = size;
	return p;
}

/**
	\param fileName relative file name (in/out)
	\return false, if error occured
	Convert relative file name to full
*/
bool pl::FileManager::convertToFullPath(pl::Filepath& fileName)
{
	if (_pImpl == NULL)
	{
		PL_ASSERTW(false, PL_T("File manager was not inited"));
		return false;
	}

	if (fileName.empty())
	{
		PL_ASSERTW(FALSE, PL_T("Wrong agruments"));
		return false;
	}

	pl::Filepath fileNameResult;
	bool rc;
	pl::Drive* pDrive = _findHandleDrive(fileName, fileNameResult);
	if (pDrive != NULL)
	{
		// This name already full or can be trasnalted
		rc = pDrive->handleTrasnlation(fileNameResult);
	}
	else
	{
		rc = _pImpl->convertToFullPath(fileNameResult);
	}
	fileName = fileNameResult;
	return rc;
}

/**
	Release free interface
*/
void pl::FindFile::release()
{
	PL_DELETE this;
	return;
}

/**
	\param list return value
	\param mask mask to find files
	\param recursive true, if looking in subfolders
	\return false, if error occured
	Put all files with full path into the vector
*/
bool pl::FindFile::findFiles(StdVector<Filepath>& list, const pl::Filepath& mask, bool recursive)
{
	pl::Filepath path = pl::removeFilename(mask);
	pl::FindFile* pFind = FileManager::findFile(mask);

	if (pFind != NULL)
	{
		while (!pFind->isFinished())
		{
			if (pFind->getFileAttrib() & FILE_ATTR_DIRECTORY)
			{
				continue;
			}
			pl::Filepath newFile = pl::mergeFilename(path, pFind->getFileName());
			list.pushBack(newFile);
		}
		pFind->release();
	}

	if (recursive)
	{
		// Looking in subfolders
		pFind = FileManager::findFile(pl::mergeFilename(path, PL_T("*.*")));

		while (pFind != NULL && !pFind->isFinished())
		{
			if (!(pFind->getFileAttrib() & FILE_ATTR_DIRECTORY))
			{
				continue;
			}
			if (pFind->getFileName() == PL_T(".") || pFind->getFileName() == PL_T(".."))
			{
				continue;
			}

			pl::Filepath newDir = pl::mergeFilename(path, pFind->getFileName());
			newDir = pl::mergeFilename(newDir, pl::extractFilename(mask));
			if (!findFiles(list, newDir, recursive))
			{
				return false;
			}
		}
		pFind->release();

	}
	return true;
}

//
// End of file 'pulsar_file_manager.cpp'
//
